﻿Public Class TreeView
    Inherits Windows.Forms.TreeView

    Dim WithEvents Timer As New Timer With {.Interval = 40}

    Sub New()
        DrawMode = TreeViewDrawMode.Normal
        'SetStyle(ControlStyles.AllPaintingInWmPaint, True)
        'SetStyle(ControlStyles.UserPaint, True)
        SetStyle(ControlStyles.OptimizedDoubleBuffer, True)
        SetStyle(ControlStyles.ResizeRedraw, True)
    End Sub

    Private Sub TreeView_BeforeSelect(ByVal sender As Object, ByVal e As System.Windows.Forms.TreeViewCancelEventArgs) Handles Me.BeforeSelect
        e.Cancel = e.Node.Text = ""
    End Sub

    Private Sub TreeView_GotFocus(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.GotFocus, Me.MouseEnter
        FadingOut = False
        Timer.Enabled = True
    End Sub

    Private Sub TreeView_MouseLeave(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.MouseLeave
        HotItem = Nothing
    End Sub

    Private Sub TreeView_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseMove
        HotItem = GetNodeAt(e.X, e.Y)
        If Not HotItem Is Nothing Then PlusHovered = (New Rectangle(HotItem.Level * Indent + 3, HotItem.Bounds.Top, HotItem.Bounds.Height - 1, HotItem.Bounds.Height)).Contains(e.Location)
    End Sub

    Dim m_HotItem As TreeNode
    Property HotItem As TreeNode
        Get
            Return m_HotItem
        End Get
        Set(ByVal Value As TreeNode)
            If Value Is HotItem Then Exit Property
            If Not HotItem Is Nothing Then Invalidate(New Rectangle(0, HotItem.Bounds.Top, Width, HotItem.Bounds.Height))
            m_HotItem = Value
            If Not HotItem Is Nothing Then Invalidate(New Rectangle(0, HotItem.Bounds.Top, Width, HotItem.Bounds.Height))
        End Set
    End Property

    Dim m_PopupNode As TreeNode
    Property PopupNode As TreeNode
        Get
            Return m_PopupNode
        End Get
        Set(ByVal Value As TreeNode)
            If Value Is PopupNode Then Exit Property
            If Not PopupNode Is Nothing Then Invalidate(New Rectangle(0, PopupNode.Bounds.Top, Width, PopupNode.Bounds.Height))
            m_PopupNode = Value
            If Not PopupNode Is Nothing Then Invalidate(New Rectangle(0, PopupNode.Bounds.Top, Width, PopupNode.Bounds.Height))
        End Set
    End Property

    Dim m_DropTarget As TreeNode
    Property DropTarget As TreeNode
        Get
            Return m_DropTarget
        End Get
        Set(ByVal Value As TreeNode)
            If Value Is DropTarget Then Exit Property
            If Not DropTarget Is Nothing Then Invalidate(New Rectangle(0, DropTarget.Bounds.Top, Width, DropTarget.Bounds.Height))
            m_DropTarget = Value
            If Not DropTarget Is Nothing Then Invalidate(New Rectangle(0, DropTarget.Bounds.Top, Width, DropTarget.Bounds.Height))
        End Set
    End Property

    ReadOnly Property ActiveNode As TreeNode
        Get
            If Not PopupNode Is Nothing Then Return PopupNode
            Return SelectedNode
        End Get
    End Property

    'Private Const WM_ERASEBKGND As Integer = 20
    'Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
    '    If m.Msg = WM_ERASEBKGND Then m.Msg = 0
    '    MyBase.WndProc(m)
    'End Sub

    'Protected Overrides Sub OnPaintBackground(ByVal pevent As System.Windows.Forms.PaintEventArgs)
    '    MyBase.OnPaintBackground(pevent)
    'End Sub

    'Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
    '    PaintNodes(Me, e.Graphics)
    'End Sub

    Dim m_PlusHovered As Boolean
    Property PlusHovered As Boolean
        Get
            Return m_PlusHovered
        End Get
        Set(ByVal Value As Boolean)
            If PlusHovered = Value Then Exit Property
            m_PlusHovered = Value
            Invalidate(New Rectangle(0, HotItem.Bounds.Top, Width, HotItem.Bounds.Height))
        End Set
    End Property

    Sub PaintNodes(ByVal Nodes As Object, ByVal G As Graphics)
        For Each Node As TreeNode In Nodes.Nodes
            If Node.IsVisible Then
                G.FillRectangle(Brushes.White, Node.Bounds)
                RaiseEvent PaintNode(Me, Node, G, PlusHovered And Node Is HotItem, AniOpacity)
            End If
            PaintNodes(Node, G)
        Next
    End Sub

    Event PaintNode(ByVal sender As Object, ByVal Node As TreeNode, ByVal G As Graphics, ByVal PlusHovered As Boolean, ByVal Opacity As Double)

    Dim m_AniOpacity As Double
    Property AniOpacity As Double
        Get
            Return m_AniOpacity
        End Get
        Set(ByVal Value As Double)
            If Value < 0 Then Value = 0 : Timer.Enabled = False
            If Value > 1 Then Value = 1 : Timer.Enabled = False
            m_AniOpacity = Value
            Invalidate()
        End Set
    End Property

    Dim FadingOut As Boolean

    Private Sub Timer_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles Timer.Tick
        AniOpacity += If(FadingOut, -0.05, 0.3)
    End Sub


    Private Sub TreeView_LostFocus(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.LostFocus, Me.MouseLeave
        FadingOut = True
        Timer.Enabled = True
    End Sub

End Class
